//
// Copyright (c) 2011 Alex Nesterenko
//

#include "nane/core/crt/Backtrace_.h"
#include "nane/core/WinAPI/winapi.h"
#include <cstring>

namespace nane
{
    ////////////////////////////////////////////////////////////////////////////////////////////////
    static const ULONG c_maxSymbolNameLength = 256;
    typedef struct _SYMBOL_INFO 
    {
      ULONG   SizeOfStruct;
      ULONG   TypeIndex;
      ULONG64 Reserved[2];
      ULONG   Index;
      ULONG   Size;
      ULONG64 ModBase;
      ULONG   Flags;
      ULONG64 Value;
      ULONG64 Address;
      ULONG   Register;
      ULONG   Scope;
      ULONG   Tag;
      ULONG   NameLen;
      ULONG   MaxNameLen;
      CHAR    Name[c_maxSymbolNameLength];
    } SYMBOL_INFO, *PSYMBOL_INFO;
    typedef BOOL (WINAPI* TSymInitialize)(HANDLE, LPWSTR, BOOL);
    typedef BOOL (WINAPI* TSymCleanup)(HANDLE);
    typedef BOOL (WINAPI* TSymFromAddr)(HANDLE, DWORD64, PDWORD64, PSYMBOL_INFO);
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void Win32BacktraceGetSymbolsImpl( StackFrame* _stackFrames, USHORT _count )
    {
        HMODULE hDbghelp = ::LoadLibraryA("Dbghelp");
        if( hDbghelp != NULL )
        {
            TSymInitialize pSymInitialize = reinterpret_cast<TSymInitialize>(::GetProcAddress(hDbghelp, "SymInitialize"));
            TSymCleanup pSymCleanup = reinterpret_cast<TSymCleanup>(::GetProcAddress(hDbghelp, "SymCleanup"));
            TSymFromAddr pSymFromAddr = reinterpret_cast<TSymFromAddr>(::GetProcAddress(hDbghelp, "SymFromAddr"));
            if( pSymInitialize != NULL 
                && pSymCleanup != NULL 
                && pSymFromAddr != NULL )
            {
                HANDLE hProcess = GetCurrentProcess();
                BOOL r = pSymInitialize(hProcess, NULL, TRUE);
                SYMBOL_INFO symbol;
                DWORD64 disp = 0;
                symbol.SizeOfStruct = sizeof(SYMBOL_INFO)-c_maxSymbolNameLength;
                symbol.MaxNameLen = c_maxSymbolNameLength;
                symbol.Name[0] = '\0';
                for( USHORT i = 0; i < _count; ++i )
                {
                    DWORD64 addr = reinterpret_cast<DWORD64>(_stackFrames[i].address);
                    BOOL result = pSymFromAddr(hProcess, addr, &disp, &symbol);
                    if( result != FALSE && symbol.Name > 0 )
                    {
                        _stackFrames[i].text = new char[symbol.NameLen+1];
                        std::strcpy(_stackFrames[i].text, symbol.Name);
                    }
                }
                pSymCleanup(hProcess);
            }
            ::FreeLibrary(hDbghelp);
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
}   // namespace nane
